﻿using System;
using System.Text;
using GLib;

namespace YanhuaMMO {


    /// <summary>
    /// This class handles byte buffer arrays.
    /// </summary>
    public class ByteBuffer
    {
        /// <summary>
        /// The byte buffer.
        /// </summary>
        public byte[] Buffer;

        public int Length;

        public int ReadableBytes => Length - Position;

        /// <summary>
        /// Returns the buffer size in bytes.
        /// </summary>
        
        public int Size => Buffer.Length;

        /// <summary>
        /// The reading and writing position.
        /// </summary>
        public int Position { get; set; }

        public ByteBuffer()
        {
            Length = 0;
            Position = 0;
        }

        public ByteBuffer(int size)
        {
            Buffer = new byte[size];
            Length = 0;
            Position = 0;
        }
        /// <summary>
        /// Create a MessageBuffer with an existing byte buffer.
        /// </summary>
        /// <param name="buffer">The byte buffer to use.</param>
        public ByteBuffer(byte[] buffer)
        {
            Buffer = buffer;
            Length = buffer.Length;
            Position = 0;
        }

        public ByteBuffer(byte[] buffer,int length)
        {
            Buffer = buffer;
            Length = length;
            Position = 0;
        }

        /// <summary>
        /// Reads a byte from the current buffer position.
        /// </summary>
        /// <returns>Returns the value.</returns>
        public byte ReadByte()
        {
            var value = Buffer[Position];
            Position++;

            return value;
        }
        public byte[] ReadBytes(int numOfBytes)
        {
            var index = Position;
            var value = new byte[numOfBytes];

            for (var i = 0; i < numOfBytes; i++)
            {
                value[i] = Buffer[index + i];
            }

            Position += numOfBytes;

            return value;
        }
        public byte[] ReadBytes(int index, int numOfBytes)
        {
            var value = new byte[numOfBytes];

            for (var i = 0; i < numOfBytes; i++)
            {
                value[i] = Buffer[index + i];
            }

            Position += numOfBytes;

            return value;
        }

        /// <summary>
        /// Reads a signed byte from the current buffer position.
        /// </summary>
        /// <returns>Returns the value.</returns>
        public sbyte ReadSByte()
        {
            var value = (sbyte) Buffer[Position];
            Position++;

            return value;
        }

        /// <summary>
        /// Reads an unsigned short from the current buffer position.
        /// </summary>
        /// <returns>Returns the value.</returns>
        public ushort ReadUInt16()
        {
            var auxByteArray = ReadBytes(Position, 2);
            BytesReverse(auxByteArray);
            var value = BitConverter.ToUInt16(auxByteArray, 0);

            return value;
        }
 
        /// <summary>
        /// 大小端转换
        /// </summary>
        /// <param name="bytes_arr"></param>
        private void BytesReverse(byte[] bytes_arr)
        {
            if (bytes_arr == null) return;
            int len = bytes_arr.Length;
            byte temp;
            for (int i = 0, j = len - 1; ; i++, j--)
            {
                if (i >= j) break;
                temp = bytes_arr[j];
                bytes_arr[j] = bytes_arr[i];
                bytes_arr[i] = temp;
             }
        }

        /// <summary>
        /// Reads a signed short from the current buffer position.
        /// </summary>
        /// <returns>Returns the value.</returns>
        public short ReadInt16()
        {
            var auxByteArray = ReadBytes(Position, 2);
            BytesReverse(auxByteArray);
            var value = BitConverter.ToInt16(auxByteArray, 0);

            return value;
        }

        /// <summary>
        /// Reads an unsigned int from the current buffer position.
        /// </summary>
        /// <returns>Returns the value.</returns>
        public uint ReadUInt32()
        {
            var auxByteArray = ReadBytes(Position, 4);
            BytesReverse(auxByteArray);
            var value = BitConverter.ToUInt32(auxByteArray, 0);

            return value;
        }

        /// <summary>
        /// Reads a signed int from the current buffer position.
        /// </summary>
        /// <returns>Returns the value.</returns>
        public int ReadInt32()
        {
            var auxByteArray = ReadBytes(Position, 4);
            BytesReverse(auxByteArray);
            var value = BitConverter.ToInt32(auxByteArray, 0);

            return value;
        }

        /// <summary>
        /// Reads a float from the current buffer position.
        /// </summary>
        /// <returns>Returns the value.</returns>
        public float ReadFloat()
        {
            var auxByteArray = ReadBytes(Position, 4);
            BytesReverse(auxByteArray);
            var value = BitConverter.ToSingle(auxByteArray, 0);

            return value;
        }
        public long ReadLong()
        {
            var auxByteArray = ReadBytes(Position, 8);
            BytesReverse(auxByteArray);
            var value = BitConverter.ToInt64(auxByteArray, 0);

            return value;
        }
        /// <summary>
        /// Reads a double from the current buffer position.
        /// </summary>
        /// <returns>Returns the value.</returns>
        public double ReadDouble()
        {
            var auxByteArray = ReadBytes(Position, 8);
            BytesReverse(auxByteArray);
            var value = BitConverter.ToDouble(auxByteArray, 0);
            

            return value;
        }

        /// <summary>
        /// Reads a bool from the current buffer position.
        /// </summary>
        /// <returns>Returns the value.</returns>
        public bool ReadBoolean()
        {
            var value = BitConverter.ToBoolean(Buffer, Position);
            Position++;

            return value;
        }

        /// <summary>
        /// Reads a string from the current buffer position.
        /// </summary>
        /// <returns>Returns the value.</returns>
        public string ReadString()
        {
            var auxByteArray = ReadBytes(Position, 4);
            BytesReverse(auxByteArray);
            var stringLength = BitConverter.ToUInt32(auxByteArray, 0);
            var stringByteArray = ReadBytes(Position, (int) stringLength);
            var value = Encoding.UTF8.GetString(stringByteArray);
            return value;
        }

       
        public byte[] ReadBytes()
        {
            var auxByteArray = ReadBytes(Position, 4);
            BytesReverse(auxByteArray);
            var stringLength = BitConverter.ToUInt32(auxByteArray, 0);
            var stringByteArray = ReadBytes(Position, (int) stringLength);
            return stringByteArray;
        }
        
        /// <summary>
        /// Writes a byte to the current buffer position.
        /// </summary>
        /// <param name="value">The value to write.</param>
        public void WriteByte(byte value)
        {
            Buffer[Position] = value;
            Position++;
            Length++;
        }
        public void WriteBytes(byte[] value)
        {
                
            var length = value.Length;

            Array.Copy(value, 0, Buffer, Position, length);
            Position += length;
            Length+= length;
            
        }
        /// <summary>
        /// Writes a signed byte to the current buffer position.
        /// </summary>
        /// <param name="value">The value to write.</param>
        public void WriteSByte(sbyte value)
        {
            Buffer[Position] = (byte) value;
            Position++;
            Length++;

        }

        /// <summary>
        /// Writes an unsigned short to the current buffer position.
        /// </summary>
        /// <param name="value">The value to write.</param>
        public void WriteUInt16(ushort value)
        {
            var bytes = BitConverter.GetBytes(value);
            BytesReverse(bytes);
            Array.Copy(bytes, 0, Buffer, Position, 2);
            Position += 2;
            Length +=2;

        }
        public void WriteLong(long value)
        {
            var bytes = BitConverter.GetBytes(value);
            BytesReverse(bytes);
            Array.Copy(bytes, 0, Buffer, Position, 8);
            Position += 8;
            Length +=8;

        }
        /// <summary>
        /// Writes a signed short to the current buffer position.
        /// </summary>
        /// <param name="value">The value to write.</param>
        public void WriteInt16(short value)
        {
            var bytes = BitConverter.GetBytes(value);
            BytesReverse(bytes);
            Array.Copy(bytes, 0, Buffer, Position, 2);
            Position += 2;
            Length +=2;

        }

       
        /// <summary>
        /// Writes an unsigned int to the current buffer position.
        /// </summary>
        /// <param name="value">The value to write.</param>
        public void WriteUInt32(uint value)
        {
            var bytes = BitConverter.GetBytes(value);
            BytesReverse(bytes);
            Array.Copy(bytes, 0, Buffer, Position, 4);
            Position += 4;
            Length +=4;

        }
        /// <summary>
        /// Writes a signed int to the current buffer position.
        /// </summary>
        /// <param name="value">The value to write.</param>
        public void WriteInt32(int value)
        {
            var bytes = BitConverter.GetBytes(value);
            BytesReverse(bytes);
            Array.Copy(bytes, 0, Buffer, Position, 4);
            Position += 4;
            Length +=4;

        }
        public void SetInt(int offset, int value)
        {
            var bytes = BitConverter.GetBytes(value);
            BytesReverse(bytes);
            Array.Copy(bytes, 0, Buffer, offset, 4);
        }
        /// <summary>
        /// Writes a float to the current buffer position.
        /// </summary>
        /// <param name="value">The value to write.</param>
        public void WriteFloat(float value)
        {
            var bytes = BitConverter.GetBytes(value);
            BytesReverse(bytes);
            Array.Copy(bytes, 0, Buffer, Position, 4);
            Position += 4;
            Length +=4;

        }

        /// <summary>
        /// Writes a double to the current buffer position.
        /// </summary>
        /// <param name="value">The value to write.</param>
        public void WriteDouble(double value)
        {
            var bytes = BitConverter.GetBytes(value);
            BytesReverse(bytes);
            Array.Copy(bytes, 0, Buffer, Position, 8);
            Position += 8;
            Length +=8;

        }

        /// <summary>
        /// Writes a boolean to the current buffer position.
        /// </summary>
        /// <param name="value">The value to write.</param>
        public void WriteBoolean(bool value)
        {
            var bytes = BitConverter.GetBytes(value);
            BytesReverse(bytes);
            Array.Copy(bytes, 0, Buffer, Position, 1);
            Position += 1;
            Length +=1;

        }

        /// <summary>
        /// Writes a null terminated string to the current buffer position.
        /// </summary>
        /// <param name="value">The value to write.</param>
        public void WriteString(string value)
        {
            var length = value.Length + 4;
            var toWrite = new byte[length];
            var stringLength = BitConverter.GetBytes((UInt32) value.Length);
            BytesReverse(stringLength);

            Array.Copy(stringLength, 0, toWrite, 0, 4);
            Array.Copy(Encoding.UTF8.GetBytes(value), 0, toWrite, 4, value.Length);
            
            Array.Copy(toWrite, 0, Buffer, Position, length);
            Position += length;
            Length +=length;

        }

        public virtual void FreeBuffer()
        {

        }
    }

    public class SendByteBuffer : ByteBuffer
    {
        private static BufferPool _bufferPool = new BufferPool();
        private int _index = -1;
        public SendByteBuffer(int s) : base(s)
        {
            _index = -1;
        }

        public SendByteBuffer()
        {
            _index = (int)_bufferPool.GetBufferIndex();
            Buffer = _bufferPool.GetBuffer((uint) _index);
            Length = 0;
            Position = 0;
        }

        public override void FreeBuffer()
        {
            if (_index >= 0)
            {
                _bufferPool.FreeBuffer((uint)_index);
            }
        }
    }
}